package com.simple.parser;

import cn.hutool.db.Entity;
import com.simple.config.SyncTaskConfig;
import com.simple.parser.model.AdvanceUpdateContext;
import com.simple.util.ExpressionsUtil;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.update.Update;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 项目名称：foursmile-local-etl
 * 类名称：SqlGenerateParser
 * 类描述：SqlGenerateParser
 * 创建时间：2022/10/20
 *
 * @author jiangjunjie   (E-mail:jiangjunjie@foursmile001.com)
 * @version v1.0
 */
public class SqlGenerateParser extends ContrastParserTemplate {


    private StringBuilder stringBuilder = new StringBuilder();


    @Override
    protected void taskPrep(SyncTaskConfig syncTaskConfig) {
        stringBuilder.append("# ").append(syncTaskConfig.getTaskName()).append("\n");
    }

    @Override
    protected void handleInsertList(List<Entity> insertList, SyncTaskConfig syncTaskConfig) {
        String sql;
        try {
            sql = this.buildInsertSql(insertList, syncTaskConfig);
        } catch (InvocationTargetException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        stringBuilder.append(sql);
    }


    @Override
    protected void handleDeleteList(List<Entity> deleteList, SyncTaskConfig syncTaskConfig) {
        String sql;
        try {
            sql = this.buildDeleteSql(deleteList, syncTaskConfig);
        } catch (InvocationTargetException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        stringBuilder.append(sql);
    }

    @Override
    protected void handleUpdateList(List<AdvanceUpdateContext> updateContextList, SyncTaskConfig syncTaskConfig) {
        try {
            for (AdvanceUpdateContext advanceUpdateContext : updateContextList) {

                stringBuilder.append(this.buildUpdateSql(advanceUpdateContext, syncTaskConfig));

            }
        } catch (InvocationTargetException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String toString() {
        return stringBuilder.toString();
    }


    private String buildUpdateSql(AdvanceUpdateContext advanceUpdateContext, SyncTaskConfig syncTaskConfig) throws InvocationTargetException, IllegalAccessException {
        SyncTaskConfig.ContrastStrategy contrastStrategy = syncTaskConfig.getContrastStrategy();
        Entity entity = advanceUpdateContext.getNewData();
        List<AdvanceUpdateContext.Item> items = advanceUpdateContext.getItems();
        List<String> columnList = items.stream().map(AdvanceUpdateContext.Item::getColumn).collect(Collectors.toList());
        List<Column> jspColumnList = columnList.stream()
                .map(i -> "`" + i + "`")
                .map(Column::new)
                .collect(Collectors.toList());

        Table table = new Table();
        table.setName(syncTaskConfig.getTableName());
        Update update = new Update();
        update.setTable(table);

        Expression whereExpression = null;
        for (String key : contrastStrategy.getPrimaryKey()) {
            EqualsTo equalsTo = new EqualsTo().withLeftExpression(new Column(table, key))
                    .withRightExpression(ExpressionsUtil.getExpression(entity.get(key)));
            if (whereExpression == null) {
                whereExpression = equalsTo;
            } else {
                whereExpression = new AndExpression().withLeftExpression(whereExpression).withRightExpression(equalsTo);
            }
        }

        List<Expression> expressions = new ArrayList<>();
        for (AdvanceUpdateContext.Item item : items) {
            expressions.add(ExpressionsUtil.getExpression(item.getOldData()));
        }

        update.withColumns(jspColumnList)
                .withExpressions(expressions)
                .withWhere(whereExpression);
        return update + ";\n";
    }

    private String buildInsertSql(List<Entity> insertList, SyncTaskConfig syncTaskConfig) throws InvocationTargetException, IllegalAccessException {
        if (insertList.isEmpty()) {
            return "";
        }
        String tableName = syncTaskConfig.getTableName();
        Table table = new Table(tableName);
        if (syncTaskConfig.getContrastStrategy().getBulkInsert()) {
            Insert insert = new Insert();
            insert.setTable(table);

            List<String> columnList = new ArrayList<>(insertList.get(0).keySet()).stream()
                    .filter(str -> !syncTaskConfig.getContrastStrategy().getInsertIgnore().contains(str))
                    .collect(Collectors.toList());
            List<Column> jspColumnList = columnList.stream()
                    .map(i -> "`" + i + "`")
                    .map(Column::new)
                    .collect(Collectors.toList());
            insert.setColumns(jspColumnList);
            MultiExpressionList multiExpressionList = new MultiExpressionList();
            for (Entity entity : insertList) {
                List<Expression> expressions = new ArrayList<>();
                for (String column : columnList) {
                    Object value = entity.get(column);
                    expressions.add(ExpressionsUtil.getExpression(value));
                }
                multiExpressionList.addExpressionList(expressions);
            }
            insert.setItemsList(multiExpressionList);

            return insert + ";\n";
        } else {
            StringBuilder insertSql = new StringBuilder();
            for (Entity entity : insertList) {
                Insert insert = new Insert();
                insert.setTable(table);

                List<String> columnList = new ArrayList<>(insertList.get(0).keySet()).stream()
                        .filter(str -> !syncTaskConfig.getContrastStrategy().getInsertIgnore().contains(str))
                        .collect(Collectors.toList());
                List<Column> jspColumnList = columnList.stream()
                        .map(i -> "`" + i + "`")
                        .map(Column::new)
                        .collect(Collectors.toList());
                insert.setColumns(jspColumnList);
                MultiExpressionList multiExpressionList = new MultiExpressionList();
                List<Expression> expressions = new ArrayList<>();
                for (String column : columnList) {
                    Object value = entity.get(column);
                    expressions.add(ExpressionsUtil.getExpression(value));
                }
                multiExpressionList.addExpressionList(expressions);
                insert.setItemsList(multiExpressionList);
                insertSql.append(insert).append(";\n");
            }
            return insertSql.toString();
        }


    }

    private String buildDeleteSql(List<Entity> deleteList, SyncTaskConfig syncTaskConfig) throws InvocationTargetException, IllegalAccessException {
        if (deleteList.isEmpty()) {
            return "";
        }
        String tableName = syncTaskConfig.getTableName();
        SyncTaskConfig.ContrastStrategy contrastStrategy = syncTaskConfig.getContrastStrategy();

        Table table = new Table(tableName);

        if (syncTaskConfig.getContrastStrategy().getBulkDelete()) {
            Delete delete = new Delete();
            delete.setTable(table);
            Expression whereExpression = null;
            for (Entity entity : deleteList) {
                Expression primaryKeyExpression = null;
                for (String key : contrastStrategy.getPrimaryKey()) {
                    EqualsTo equalsTo = new EqualsTo().withLeftExpression(new Column(table, key))
                            .withRightExpression(ExpressionsUtil.getExpression(entity.get(key)));
                    if (primaryKeyExpression == null) {
                        primaryKeyExpression = equalsTo;
                    } else {
                        primaryKeyExpression = new AndExpression().withLeftExpression(primaryKeyExpression).withRightExpression(equalsTo);
                    }
                }
                if (whereExpression == null) {
                    whereExpression = primaryKeyExpression;
                } else {
                    whereExpression = new OrExpression().withLeftExpression(whereExpression).withRightExpression(primaryKeyExpression);
                }
            }
            delete.withWhere(whereExpression);
            return delete + ";\n";
        } else {
            StringBuilder sql = new StringBuilder();
            for (Entity entity : deleteList) {
                Delete delete = new Delete();
                delete.setTable(table);
                Expression whereExpression = null;
                for (String key : contrastStrategy.getPrimaryKey()) {
                    EqualsTo equalsTo = new EqualsTo().withLeftExpression(new Column(table, key))
                            .withRightExpression(ExpressionsUtil.getExpression(entity.get(key)));
                    if (whereExpression == null) {
                        whereExpression = equalsTo;
                    } else {
                        whereExpression = new AndExpression().withLeftExpression(whereExpression).withRightExpression(equalsTo);
                    }
                }
                delete.withWhere(whereExpression);
                sql.append(delete).append(";\n");
            }
            return sql.toString();
        }

    }


}
